/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Miscellaneous operations with raster.                         *
 * modul:       imageop.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stddef.h>
#include <stdio.h>

#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "typedfs.h"
#include "image.h"
#include "ras_prot.h"
#include "imageop.h"
#include "img_tool.h"

#include "matrix.h"


//#include <alloc.h>

#ifndef MIN
 #define MIN(a,b) ((a)<(b) ? (a) : (b))
#endif


////////////////////////////////////////////////////////////////////

Image PeelPlane(const Image Img, int PlaneNum)
{
Raster2DAbstract *Raster=NULL;
Image NewImg;

 if(Img.Raster!=NULL)
  {
  Raster = CreateRaster2D(Img.Raster->Size1D,Img.Raster->Size2D,1);
  if(Raster)
    for(unsigned y=0; y<Img.Raster->Size2D; y++)	// main row loop
      {
      Img.Raster->GetRowRaster(y)->Peel1Bit(Raster->GetRow(y),PlaneNum);
      }
  }

  if(Raster!=NULL) NewImg.AttachRaster(Raster);
  if(Img.Palette!=NULL) NewImg.AttachPalette(Img.Palette);

return(NewImg);
}



Image Peel8bit(const Image Img, int PlaneNum8)
{
Raster2DAbstract *Raster=NULL;
Image NewImg;

 if(Img.Raster!=NULL)
  {
  PlaneNum8 *= 8;
  Raster = CreateRaster2D(Img.Raster->Size1D,Img.Raster->Size2D,8);

  if(Raster)
    for(unsigned y=0; y<Img.Raster->Size2D; y++)	// main row loop
      {
      Img.Raster->GetRowRaster(y)->Peel8Bit((unsigned char*)Raster->GetRow(y),PlaneNum8);
      }
  }

  if(Raster!=NULL) NewImg.AttachRaster(Raster);
  if(Img.Palette!=NULL) NewImg.AttachPalette(Img.Palette);

return(NewImg);
}


////////////////////////////////////////////////////////////////////


/// Filter image using a mask with given size.
Image Mask(Image *pIm_in, int size)
{
Image im_out, *pImOut=&im_out;
uint16_t k2;
uint32_t k;
uint16_t i,j;
uint16_t x,y;
int y1,y2,x1,x2;
uint32_t aLl;
uint32_t *ptrLine;
uint8_t *Sptr;
uint32_t *SptrAl,*SptrAr;
Raster1D_8Bit ptrLineB;
uint8_t color,clr;

 if(size==0)  return im_out;

 while(pIm_in!=NULL)
 {
   color = 1;
   if(pIm_in->ImageType()==ImageTrueColor)
   {
     color = 3;
     //  Obr2.typ:='C';
     //if AlineProc<>nil then AlineProc^.pases := AlineProc^.pases*3;
   }

   if(pImOut->Raster != NULL)
   {
     pImOut->Next = new Image;
     pImOut = pImOut->Next;
   }
   pImOut->Create(pIm_in->Raster->GetSize1D(), pIm_in->Raster->Size2D, pIm_in->Raster->GetPlanes());
   if(pImOut->Raster==NULL || pImOut->Raster->Data1D==NULL) break;

   //if( AlineProc!=NULL) AlineProc->InitPassing(Obr1.y,'Filtering by MASK');

   ptrLine = (uint32_t *)malloc(4*pIm_in->Raster->GetSize1D());
   ptrLineB.Allocate1D(pIm_in->Raster->GetSize1D());

   k = size*size;
   k2 = k/2;
   x = size/2 + 1;
   y = size/2 + 1;

   for(clr=0;clr<color;clr++)
   {
     y1 = x + 1;
     if(size<=2) y1--;		//pocet pridanych radek

     memset(ptrLine,0,4*pIm_in->Raster->GetSize1D());
     if(color==1)
        ptrLineB.Set(*pIm_in->Raster->GetRowRaster(0));
     else
       pIm_in->Raster->GetRowRaster(0)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);


     for(j=0; j<y1; j++) 
       AddLu32u8(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); /* ukradek=y1*ukradekW*/

     for(y1=1;y1<=(int)(size+1)/2 - 2;y1++)
     {		// pridani pripravnych radku}
       if(color==1)
         ptrLineB.Set(*pIm_in->Raster->GetRowRaster(y1));
       else
         pIm_in->Raster->GetRowRaster(y1)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
       AddLu32u8(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D);
     }

     for(i=0;i<pImOut->Raster->Size2D;i++)	// main filtration loop
     {
       y1 = (int)(i)-y;
       y2 = y1+size;
       if(y1<0) y1=0;
       if(y2>=pImOut->Raster->Size2D) y2 = pImOut->Raster->Size2D - 1;

      if(color==1)
        ptrLineB.Set(*pIm_in->Raster->GetRowRaster(y1));
     else
        pIm_in->Raster->GetRowRaster(y1)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
      SubLu32u8(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); //vyjmuti posledniho radku y1

     if(color==1)
       ptrLineB.Set(*pIm_in->Raster->GetRowRaster(y2));
     else
       pIm_in->Raster->GetRowRaster(y2)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
     AddLu32u8(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); //pridani noveho radku y2
		//zpracovani obsahu radku
     x1 = y + 1;
     if(size<=2) x1--;		// pocet pridanych sloupcu
     aLl = x1 * ptrLine[0];
     for(x2=1;x2<=(int)(size+1)/2 - 2;x2++) aLl += ptrLine[x2];
 
     Sptr = (uint8_t *)ptrLineB.Data1D;		//vypocet masky v radku
     x1 = -(int)(x);
     x2 = MIN(x1+size,ptrLineB.GetSize1D()-1);
     SptrAl = ptrLine;
     SptrAr = ptrLine + x2;
     for(j=0;j<ptrLineB.GetSize1D();j++)
     {
       aLl += *SptrAr;
       aLl -= *SptrAl;

       *Sptr = (aLl + k2) / k;

       if(x1>=0)	SptrAl++;
       if(x2<ptrLineB.GetSize1D()-1) SptrAr++;
       Sptr++;
       x1++;
       x2++;
     }

     if(color==1)		// data storage
       ptrLineB.Get(*pImOut->Raster->GetRowRaster(i));
     else
       pImOut->Raster->GetRowRaster(i)->Join8Bit((uint8_t *)ptrLineB.Data1D,8*clr);

     //if AlineProc<>nil then AlineProc^.NextLine;	{Dialog s uzivatelem}
    }
   }
   ptrLineB.Erase();
   free(ptrLine);  ptrLine=NULL;

   pIm_in = pIm_in->Next;
 }

return(im_out);
}


///////////////////////////////////////////////////////////////////////////

extern "C" {
typedef struct
{
  uint16_t MaxSz;
  uint16_t Size;
  uint16_t data[];
} TMed;

#ifdef _DEBUG
void PrintElement(const TMed *pMed)
{
  putchar('[');
  for(unsigned i=0; i<pMed->Size; i++)
  {
    if(i>0) putchar(',');
    printf("%u",pMed->data[i]);
  }
  putchar(']');
}
#endif


void InsertElement(uint16_t element, TMed *pMed)
{
unsigned i;
uint16_t *pData;
 if(pMed!=NULL && pMed->Size<pMed->MaxSz)
 {
   pData = pMed->data;
   i = ++pMed->Size;

   while(i>0)
   {
     i--;
     if(i==0)
     {
       pData[0] = element;
       break;
     }
     if(pData[i-1]<=element)
     {
       pData[i] = element;
       break;
     }
     pData[i] = pData[i-1];
   }
 }
}


void RemoveElement(uint16_t element, TMed *pMed)
{
uint16_t i;
uint16_t *pData;
 if(pMed)
 {
   pData = pMed->data;
   for(i=0; i<pMed->Size; i++)
   {
      if(pData[i]==element)
      {
        i++;
        while(i<pMed->Size)
        {
          pData[i-1] = pData[i];
          i++;
        }
        pMed->Size--;
        break;
      }
    }
   }
 }
}


void RemoveBlock(const TMed *pRmMed, TMed *pMed)
{
uint16_t i,j, r;
uint16_t *pData;
const uint16_t *pRmData;
 if(pMed==NULL || pRmMed==NULL) return;

 pData = &(pMed->data[0]);
 pRmData = &(pRmMed->data[0]);

 j = 0;
 r = 0;
 for(i=0; i<pMed->Size; i++)
 {
   if(r<pRmMed->Size && pData[i]==pRmData[r])
   {
     r++;
   }
   else
   {
     pData[j] = pData[i];
     j++;
   }
 }
 pMed->Size = j;
}


void InsertBlock(const TMed *pAddMed, TMed *pMed)
{
uint16_t i,j, a;
uint16_t *pData;
const uint16_t *pAddData;
 if(pMed==NULL || pAddMed==NULL) return;

 pData = &(pMed->data[0]);
 pAddData = &(pAddMed->data[0]);

 j = pMed->Size;
 a = pAddMed->Size;
 i = j + a;
 if(i>pMed->MaxSz) i=pMed->MaxSz;
 pMed->Size = i;

 while(i>0)
 {
   i--;
   if(j==0)
   {
     pData[i] = pAddData[--a];
     continue;
   }
   if(a==0)
   {
     //pData[i] = pData[--j];
     //continue;
     break;	// all data needad ara already here; i=j-1
   }

   if(pData[j-1] >= pAddData[a-1])
     pData[i] = pData[--j];
   else
     pData[i] = pAddData[--a];
 }
}


inline unsigned FitInterval(int x, int MaxX)
{
  if(x<=0) return 0;
  if(x>=MaxX) return MaxX-1;
  return (unsigned)x;
}


Image Median(const Image &im_in, int n)
{
int i,j;
int x,x1,y,y1;
uint16_t q;
uint8_t Color;
TMed *MediR;
TMed *MediG=NULL, *MediB=NULL;
TMed *Lmed;
TMed *LmedG=NULL, *LmedB=NULL;
Image im_out;
RGBQuad RGB;
const Raster1DAbstract *pRowLine;

 if(n==0 || im_in.Raster==NULL) return im_out;

 Color = 1;
 if(im_in.ImageType()==ImageTrueColor)
 {
   Color = 3;
   //  Obr2.typ:='C';
   im_out.Create(im_in.Raster->GetSize1D(), im_in.Raster->Size2D, ImageTrueColor|(im_in.Raster->GetPlanes()/im_in.Raster->Channels()));
 }
 else
   im_out.Create(im_in.Raster->GetSize1D(),im_in.Raster->Size2D,im_in.Raster->GetPlanes());

 if(im_out.Raster==NULL || im_out.Raster->Data1D==NULL) return im_out;

 // if AlineProc<>nil then AlineProc^.InitPassing(Obr1.x,'Filtering by MEDIAN');

 q = n*n;
 MediR = (TMed*)malloc(2*q+offsetof(TMed,data));
 Lmed = (TMed*)malloc(2*n+offsetof(TMed,data));
 if(MediR==NULL || Lmed==NULL) goto Fail;
 MediR->MaxSz = q;  
 Lmed->MaxSz = n;
 if(Color>1)
 {
   MediG = (TMed*)malloc(2*q+offsetof(TMed,data));
   MediB = (TMed*)malloc(2*q+offsetof(TMed,data));
   if(MediG==NULL || MediB==NULL) goto Fail;
   MediG->MaxSz = MediB->MaxSz = q;
   LmedG = (TMed*)malloc(2*n+offsetof(TMed,data));
   LmedB = (TMed*)malloc(2*n+offsetof(TMed,data));
   if(LmedG==NULL || LmedB==NULL) goto Fail;
   LmedG->MaxSz = LmedB->MaxSz = n;
 }

 q /= 2;
 x1 = y1 =  n / 2;

 {
   for(i=0; i<im_in.Raster->Size1D; i++)		// main filter loop
   {
     MediR->Size = 0;
     if(Color>1) MediG->Size=MediB->Size = 0;
     for(x=i-x1; x<i-x1+n; x++)
     {
       const int xx = FitInterval(x,im_in.Raster->Size1D);
       if(Color<=1)
       {
	 for(y=-y1-1; y<=-y1+n-2; y++)			// add one row before mask that will be removed
         {
	   //printf("IE:%d,%d,%u",x,y,im_in.Raster->GetValue2D(x,y));
	   InsertElement(im_in.Raster->GetValue2D(xx,(y<0)?0:y),MediR);
         }
       }
       else
       {         
 	 for(y=-y1-1; y<=-y1+n-2; y++)
         {
           im_in.Raster->Get(xx, (y<0)?0:y, &RGB);
	   InsertElement(RGB.R,MediR);
           InsertElement(RGB.G,MediG);
           InsertElement(RGB.B,MediB);
         }
        }
     }

     //if(n>5)		// Plain unblocked algoritmh has no benefit in C++, removed.
     for(j=0; j<im_in.Raster->Size2D; j++)
     {
       y = j-y1-1;
       pRowLine = im_in.Raster->GetRowRaster((y<0)?0:y);  
       if(pRowLine==NULL) continue;
       Lmed->Size = 0;

       if(Color<=1)
       {         
 	 for(x=i-x1; x<i-x1+n; x++) InsertElement(pRowLine->GetValue1D(FitInterval(x,im_in.Raster->Size1D)),Lmed);
       }
       else
       {
         LmedB->Size = LmedG->Size = 0;
	 for(x=i-x1; x<i-x1+n; x++) 
         {
           pRowLine->Get(FitInterval(x,im_in.Raster->Size1D), &RGB);
           InsertElement(RGB.R, Lmed);
           InsertElement(RGB.G, LmedG);
           InsertElement(RGB.B, LmedB);
         }
         RemoveBlock(LmedG,MediG);
         RemoveBlock(LmedB,MediB);
       }
        
       RemoveBlock(Lmed,MediR);

       y += n;
       pRowLine = im_in.Raster->GetRowRaster((y>=im_in.Raster->Size2D)?(im_in.Raster->Size2D-1):y);
       if(pRowLine==NULL) continue;
       Lmed->Size = 0;
       if(Color<=1)
       {
         for(x=i-x1; x<i-x1+n; x++)
            InsertElement(pRowLine->GetValue1D(FitInterval(x,im_in.Raster->Size1D)),Lmed);
       }
       else
       {
         LmedB->Size = LmedG->Size = 0;
         for(x=i-x1; x<i-x1+n; x++)
         {
           pRowLine->Get(FitInterval(x,im_in.Raster->Size1D), &RGB);
           InsertElement(RGB.R,Lmed);
           InsertElement(RGB.G,LmedG);
           InsertElement(RGB.B,LmedB);
         }
         InsertBlock(LmedG,MediG);
         InsertBlock(LmedB,MediB);
       }

        //PrintElement(Lmed);PrintElement(MediR);
       InsertBlock(Lmed,MediR);
        //PrintElement(MediR);

       if(Color<=1)
	  im_out.Raster->SetValue2D(i,j,MediR->data[q]);		// data store
       else
       {
         RGB.R = MediR->data[q];
         RGB.G = MediG->data[q];
         RGB.B = MediB->data[q];
         im_out.Raster->Set(i,j,&RGB);
       }
      }
      //if AlineProc<>nil then AlineProc^.NextLine;
   }
 }

Fail:
 if(Lmed) free(Lmed);
 if(LmedG) free(LmedG);
 if(LmedB) free(LmedB);
 if(MediG) free(MediR);
 if(MediG) free(MediG);
 if(MediB) free(MediB);

 return im_out;
}


///////////////////////////////////////////////////////////////////////////

Image Threshold(const Image *pImg, unsigned ThrValue)
{
Image NewImg, *pNewImg=&NewImg;
unsigned x, y;

 while(pImg != NULL)
 {
   if(pImg->Raster!=NULL && pImg->Raster->Size1D>0 && pImg->Raster->Size2D!=0)
   {
     if(pNewImg->Raster!=NULL)
     {
       pNewImg->Next = new Image;
       pNewImg = pNewImg->Next;
     }
     pNewImg->AttachRaster(CreateRaster2D(pImg->Raster->Size1D,pImg->Raster->Size2D,1));     
     if(pNewImg->Raster)
       for(y=0; y<pImg->Raster->Size2D; y++)		// main row loop
       {
         Raster1DAbstract * const Rsrc = pImg->Raster->GetRowRaster(y);
         Raster1DAbstract * const Rthr = pNewImg->Raster->GetRowRaster(y);

         for(x=0; x<pImg->Raster->Size1D; x++)
         {
           if(Rsrc->GetValue1D(x)>ThrValue)
              Rthr->SetValue1D(x,1);
           else
              Rthr->SetValue1D(x,0);
         }
     }
   }   
   pImg = pImg->Next;
 }
return(NewImg);
}


///////////////////////////////////////////////////////


void Convert2Gray(Image *pI)
{
Raster2DAbstract *Raster = NULL;

  while(pI!=NULL)
  {
    switch(pI->ImageType())
    {
      case ImageNone:
      case ImageGray:
      case ImagePaletteF:
      case ImageReal:
      case ImageSigned:		break;
      
      case ImagePalette:	 //2-palette,
		Raster = CreateRaster2D(pI->Raster->Size1D,pI->Raster->Size2D,8);
                if(Raster)
                {
                  for(unsigned y=0; y<Raster->Size2D; y++)		// main row loop
                  {
                    Raster1DAbstract *RSrc = pI->Raster->GetRowRaster(y);
                    Raster1DAbstract *RDest = Raster->GetRowRaster(y);
                    for(unsigned x=0; x<Raster->Size1D; x++)
                    {
                      uint32_t val = pI->Palette->GetValue1D(RSrc->GetValue1D(x));
                      val = ((val&0xFF) + ((val>>8)&0xFF) + ((val>>16)&0xFF)) / 3;
	              RDest->SetValue1D(x,val);
                    }
                  }
                  pI->AttachRaster(Raster);
                  pI->AttachPalette(NULL);
                  Raster = NULL;
                }
                break;

      case ImageTrueColor:
                Raster = CreateRaster2D(pI->Raster->Size1D,pI->Raster->Size2D,8);
                if(Raster)
                {
                  for(unsigned y=0; y<Raster->Size2D; y++)		// main row loop
                  {
                    Raster1DAbstract *RSrc = pI->Raster->GetRowRaster(y);
                    Raster1DAbstract *RDest = Raster->GetRowRaster(y);
                    for(unsigned x=0; x<Raster->Size1D; x++)
                    {
                      uint32_t val = RSrc->GetValue1D(x);
                      val = ((val&0xFF) + ((val>>8)&0xFF) + ((val>>16)&0xFF)) / 3;
	              RDest->SetValue1D(x,val);
                    }
                  }
                  pI->AttachRaster(Raster); Raster=NULL;  // Ussage count is 1; piinter could be discarded
                  pI->AttachPalette(NULL);                  
                }
                break;
		
    }
    pI = pI->Next;
  }
}


/////////////////////////////////////////////////////////////////////////////////////////////


void RollLine(Raster2DAbstract *Ras)
{
  if(Ras==NULL || Ras->Data2D==NULL || Ras->Size2D<=1) return;
  void *Ptr0 = Ras->Data2D[0];
  for(unsigned y=1; y<Ras->Size2D; y++)
  {
    Ras->Data2D[y-1] = Ras->Data2D[y];
  }
  Ras->Data2D[Ras->Size2D-1] = Ptr0;
}


#define GausDiffY \
   {I = SizeY-GausN-1;		\
   void *PLine = LB2.Data2D[I];	\
   LB2.Data2D[I] = LB1.Data2D[I-1];	\
   LB1.Data2D[I-1] = PLine;	\
   DiffVer1_u32((uint32_t*)PL, (const uint32_t*)LB2.Data2D[I-1], (const uint32_t*)LB1.Data2D[I], SizeX); }


void GausFunc(Raster2DAbstract *obr, unsigned char FuncC, unsigned char GausN)
{
uint8_t DiffN;
int  I,J,K,YI,YO,SizeY,SizeX;
Raster2D_32Bit LB1, LB2;
Raster2D_32Bit R3L;
//float R1,R2;
//float CannyScale;
Raster1DAbstract *PL,*PL1,*PL2,*PL3;
Raster1D_8Bit PL8;
uint8_t Clr, Colors;

  DiffN=0;
  switch(FuncC)
  {
    case gfGaus:    break;
    case gfGausDiffX:
    case gfGausDiffY:
    case gfGausDiff: 
    case gfCanny:
    case gfCannyFast:
    case gfGausLap  :DiffN=1;
		     break;
  }
  //if(Mult==0) Mult=0x10000;
  SizeX = obr->Size1D;
  if(SizeX<10 || obr->Size2D<10) return;

  if((FuncC!=gfCanny)&&(FuncC!=gfCannyFast))
    SizeY=1+GausN+DiffN;
  else
  {
    SizeY = 2+GausN+DiffN;
    R3L.Allocate2D(SizeX,3);
    for(I=0; I<R3L.Size2D; I++)
    {
      memset(R3L.GetRow(I),0,SizeX*sizeof(float));
    }
    //CannyScale=Mult;
    //CannyScale=4/CannyScale;
  }

  LB1.Allocate2D(SizeX,SizeY);
  LB2.Allocate2D(SizeX,SizeY);

  Colors = obr->Channels();
  if(Colors>0) PL8.Allocate1D(SizeX);

  for(Clr=0; Clr<Colors; Clr++)
  {    
    YI = -(SizeY-1);
    YO = -(2*SizeY-2);
    while(YO < (int)obr->Size2D)
    {    
      RollLine(&LB1);
      RollLine(&LB2);

      Raster1DAbstract *PL = LB1.GetRowRaster(SizeY-1);
	// Duplicate first/last row only - no reflection.
    
      if(YI<0) I=0;
      else if(YI>=obr->Size2D) I=obr->Size2D-1;
      else I=YI;      

      if(Colors==1)
         PL->Set(*obr->GetRowRaster(I));
      else
      {
        obr->GetRowRaster(I)->Peel8Bit((uint8_t *)PL8.Data1D,8*Clr);
        PL->Set(PL8);        
      }


/*	// Reflection - commented out
    if(YI<(int)obr->Size2D)
      PL->Set(*obr->GetRowRaster(labs(YI)));
    else
    {
      int YY = 2*obr->Size2D-YI-2;
      if(YY<0) YY=0;      
      PL->Set(*obr->GetRowRaster(YY));
    } */

    Gaus1D_u32((uint32_t*)PL->Data1D,SizeX,GausN);
    YI++;

    for(I=SizeY-1; I>=SizeY-GausN; I--)
    {
      void *PLine = LB2.Data2D[I];
      LB2.Data2D[I] = LB1.Data2D[I-1];
      LB1.Data2D[I-1] = PLine;
      GausVer1_u32((uint32_t*)PLine, (uint32_t*)LB1.Data2D[I], (uint32_t*)LB2.Data2D[I], (uint32_t*)LB2.Data2D[I-1], SizeX);
    }

    switch(FuncC)
    {
      case gfGaus:if(YO>=0)
                  {
                    PL = LB1.GetRowRaster(0);
                    if(Colors==1)
                      obr->GetRowRaster(YO)->Set(*PL);
                    else
                    {
                      PL8.Set(*PL);
                      obr->GetRowRaster(YO)->Join8Bit(PL8.Data1D,8*Clr);
                    }
                  }
                  break;

      case gfGausDiffX:if(YO>=0)
                  {
	            PL = LB1.GetRowRaster(0);
	            DiffHor1_u32((uint32_t*)PL->Data1D, (const uint32_t*)PL->Data1D, SizeX);
                    if(Colors==1)
	              obr->GetRowRaster(YO)->Set(*PL);
                    else
                    {
                      PL8.Set(*PL);
                      obr->GetRowRaster(YO)->Join8Bit(PL8.Data1D,8*Clr);
                    }
                  }
                  break;

      case gfGausDiffY:
	          GausDiffY
                  if(YO>=0)
                  {
	            PL = LB1.GetRowRaster(0);
                    if(Colors==1)
	                obr->GetRowRaster(YO)->Set(*PL);
                    else
                    {
                      PL8.Set(*PL);
                      obr->GetRowRaster(YO)->Join8Bit(PL8.Data1D,8*Clr);
                    }
                  }
                  break;


      case gfGausDiff :
	         GausDiffY
	         if(YO>=0)
	         {
	           I = SizeY - GausN - 2;	           
	           DiffHor1_u32((uint32_t*)LB2.GetRow(I), (uint32_t*)LB2.GetRow(I+1), SizeX);
	           AbsHor_u32((uint32_t*)LB1.GetRow(I), (uint32_t*)LB2.GetRow(I), SizeX);
	           PL = LB1.GetRowRaster(0);
                    if(Colors==1)
	                obr->GetRowRaster(YO)->Set(*PL);
                    else
                    {
                      PL8.Set(*PL);
                      obr->GetRowRaster(YO)->Join8Bit(PL8.Data1D,8*Clr);
                    }
	         }
                 break;

/*
      case gfCanny:
	GausDiffY;
	I=SizeY-GausN-2;
	DiffHor1(LB2.Data^[I],LB2.Data^[I+1],SizeX);
	PRL=R3L[0];
	PL =LB1.Data^[I];
	PL1=LB2.Data^[I];
	for I=0 to SizeX-1 do
	{
	  R1=PL1^[I];
	  R2=PL^[I];
	  PRL^[I]=sqrt(sqr(R1)+sqr(R2));
	}
	R3L[0]=R3L[1];R3L[1]=R3L[2];R3L[2]=PRL;
	if YO>=0 
	{
	  PL1=LB1.Data^[0];
	  PL2=LB2.Data^[0];
	  for I=1 to SizeX-2 do
	  {
	    if Edge(@R3L,PL1^[I],PL2^[I],I) 
	      setpixel(obr,I,YO,0)
	    else
	    {
	      R1=R3L[1]^[I]*CannyScale;
	      if R1>255  R1=255;
	      setpixel(obr,I,YO,round(R1));
	    }
	  }
	  setpixel(obr,0,YO,0);setpixel(obr,SizeX-1,YO,0);
	}
      } */

      case gfCannyFast:
	GausDiffY
	I = SizeY-GausN-2;        
	DiffHor1_u32((uint32_t*)LB2.GetRow(I), (const uint32_t*)LB2.GetRow(I+1), SizeX);	
	AbsCopy_u32((uint32_t*)R3L.GetRow(0), (uint32_t*)LB1.GetRow(1), (uint32_t*)LB2.GetRow(1), SizeX);
        RollLine(&R3L);
	if(YO>=0)
	{
/*
	  PL1=LB1.Data^[0];
	  PL2=LB2.Data^[0];

	  PL =PL1;		{ Funkce EdgeLine v Assembleru }
	  EdgeLineAsm(PL,pointer(@R3L),PL1,PL2,SizeX);
*/

          PL = LB1.GetRowRaster(0);
          if(Colors==1)
	      obr->GetRowRaster(YO)->Set(*PL);
          else
          {
            PL8.Set(*PL);
            obr->GetRowRaster(YO)->Join8Bit(PL8.Data1D,8*Clr);
          }
	}
        break;

/*
      case gfGausLap:{
	I=SizeY-GausN-1;
	PL =LB2.Data^[I];
	PL2=LB1.Data^[I-1];
	LB1.Data^[I-1]=PL;
	LB2.Data^[I]=PL2;
	DiffVer2(PL,LB2.Data^[I-1],PL2,LB1.Data^[I],SizeX);
	if YO>=0 
	{
	  I=SizeY-GausN-2;
	  PL =LB2.Data^[I];
	  PL1=LB2.Data^[I+1];
	  DiffHor2(PL,PL1,SizeX);
	  PL =LB1.Data^[I];
	  PL1=LB2.Data^[I];
	  AddHor(PL,PL1,SizeX);
	  WriteLine(obr,YO,PL,Mult,Offs);
	}
      }
*/
      }
      //if AlineProc!=nil  AlineProc^.NextLine;
      YO++;
    }
  }

  R3L.Erase();
 
  LB1.Erase();
  LB2.Erase();
}

